package fr.limsi.seven;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.Map.Entry;

/**
 * Grid2D.java Created on : 18 juin 07
 */

/**
 * @author Camille
 */
public class Grid2D {

	/**
	 * Scale type integer constant
	 */
	public static final int LINEAR_SCALE = 0;

	/**
	 * Scale type integer constant
	 */
	public static final int LOGARITHMIC_SCALE = 1;

	/**
	 * Associated {@link Pyramide2D}
	 */
	protected Pyramide2D pyramide2D;

	/**
	 * Grid current scale type
	 */
	protected int scaleType;

	/**
	 * Grid color
	 */
	protected Color gridColor = Color.GREEN;

	/**
	 * The grid range factor
	 */
	protected double rangeFactor = 1;

	/**
	 * Default constructor
	 * 
	 * @param pyramide2D
	 *            the associated {@link Pyramide2D}
	 */
	public Grid2D(Pyramide2D pyramide2D) {
		this.pyramide2D = pyramide2D;
		this.scaleType = LINEAR_SCALE;
		buildGrid();
	}

	/**
	 * Draws the grid
	 * 
	 * @param g2
	 */
	protected void drawGrid(Graphics2D g2) {

		g2.setPaint(gridColor);

		switch (scaleType) {
		case LINEAR_SCALE:
			drawLinearGrid(g2);
			break;

		case LOGARITHMIC_SCALE:
			drawLogarithmicGrid(g2);
			break;

		default:
			drawLinearGrid(g2);
		}
	}

	/**
	 * Draws a linear scaled grid
	 * 
	 * @param g2
	 */
	protected void drawLinearGrid(Graphics2D g2) {
		// Horizontal lines
		double pyramideHeight = pyramide2D.getPyramideHeight();
		double deltaH = pyramideHeight / 20.0d;

		for (double i = 0; i < pyramideHeight; i += deltaH) {
			double y = (pyramide2D.getHeight() - Pyramide2D.MARGINS) - ((float) (i / pyramideHeight) * (pyramide2D.getHeight() - (2 * Pyramide2D.MARGINS)));

			Point linePos = new Point(0, (int) y);
			pyramide2D.applyTransformations(linePos);

			g2.draw(new Line2D.Double(0, linePos.y, pyramide2D.getWidth(), linePos.y));
		}

		// Vertical lines
		double pyramideWidth = pyramide2D.getWidth();
		double deltaW = pyramideWidth / 20.0d;

		for (double i = 0; i < pyramideWidth; i += deltaW) {

			Point linePos = new Point((int) i + Pyramide2D.MARGINS, 0);
			pyramide2D.applyTransformations(linePos);

			g2.draw(new Line2D.Double(linePos.x, 0, linePos.x, pyramide2D.getHeight()));
		}
	}

	/**
	 * Draws a logarithmic scaled grid
	 * 
	 * @param g2
	 */
	protected void drawLogarithmicGrid(Graphics2D g2) {
		// Horizontal lines
		double pyramideHeight = pyramide2D.getPyramideHeight();
		double deltaH = pyramideHeight / 20.0d;

		for (double i = 0; i < pyramideHeight; i += deltaH) {
			double y = (pyramide2D.getHeight() - Pyramide2D.MARGINS) - ((float) (Math.log1p(i * rangeFactor) / Math.log1p(pyramideHeight * rangeFactor)) * pyramide2D.getHeight());

			Point linePos = new Point(0, (int) y);
			pyramide2D.applyTransformations(linePos);

			g2.draw(new Line2D.Double(0, linePos.y, pyramide2D.getWidth(), linePos.y));
		}

		// Vertical lines
		double pyramideWidth = pyramide2D.getWidth();
		double deltaW = pyramideWidth / 20.0d;

		for (double i = 0; i < pyramideWidth; i += deltaW) {

			Point linePos = new Point((int) i + Pyramide2D.MARGINS, 0);
			pyramide2D.applyTransformations(linePos);

			g2.draw(new Line2D.Double(linePos.x, 0, linePos.x, pyramide2D.getHeight()));
		}
	}

	/**
	 * Fills the grid ratios
	 */
	protected void buildGrid() {

		for (Iterator<Entry<Integer, Node2D>> iter = pyramide2D.getNodes().entrySet().iterator(); iter.hasNext();) {
			Entry<Integer, Node2D> entry = iter.next();

			double contender = 1.0d / ((Node2D) entry.getValue()).getPNode().getHeight();

			if (!Double.isInfinite(contender) && !Double.isNaN(contender)) {
				rangeFactor = Math.max(rangeFactor, contender);
			}
		}
	}

	/**
	 * Getter for the grid factor (i.e. the factor by which heights must be multiplied to match a range going higher than 1)
	 * 
	 * @return the <i>rangeFactor</i> value
	 */
	public double getRangeFactor() {
		return rangeFactor;
	}

	/**
	 * Getter for the grid color
	 * 
	 * @return the grid {@link Color}
	 */
	public Color getColor() {
		return gridColor;
	}

	/**
	 * Setter for the grid color
	 * 
	 * @param gridColor
	 *            the {@link Color} to be set
	 */
	public void setColor(Color gridColor) {
		this.gridColor = gridColor;
	}

	/**
	 * Getter for the grid scale type
	 * 
	 * @return the integer constant corresponding to the grid current scale type
	 */
	public int getScaleType() {
		return scaleType;
	}

	/**
	 * Setter for the grid scale type
	 * 
	 * @param scaleType
	 *            the integer constant corresponding to the scale type to be set
	 */
	public void setScaleType(int scaleType) {
		this.scaleType = scaleType;
	}
}
